#include <DB/IO/ReadBufferFromIStream.h>
#include <DB/IO/HashingReadBuffer.h>
#include <DB/IO/WriteBufferFromOStream.h>
#include "hashing_buffer.h"
#include <iostream>

void test(size_t data_size)
{
	std::vector<char> vec(data_size);
	char * data = &vec[0];

	for (size_t i = 0; i < data_size; ++i)
		data[i] = rand() & 255;

	uint128 reference = referenceHash(data, data_size);

	std::vector<size_t> block_sizes = {56, 128, 513, 2048, 3055, 4097, 4096};
	for (size_t read_buffer_block_size : block_sizes)
	{
		std::cout  << "block size " << read_buffer_block_size << std::endl;
		std::stringstream io;
		DB::WriteBufferFromOStream out_(io);
		DB::HashingWriteBuffer out(out_);
		out.write(data, data_size);
		out.next();

		//std::cout.write(data, data_size);
		//std::cout << std::endl;
		//std::cout << io.str() << std::endl;

		DB::ReadBufferFromIStream source(io, read_buffer_block_size);
		DB::HashingReadBuffer buf(source);

		std::vector<char> read_buf(data_size);
		buf.read(read_buf.data(), data_size);

		bool failed_to_read = false;
		for (size_t i = 0; i < data_size; ++i)
			if (read_buf[i] != vec[i])
			{
				failed_to_read = true;
			}

		if (failed_to_read)
		{
			std::cout.write(data, data_size);
			std::cout << std::endl;
			std::cout.write(read_buf.data(), data_size);
			std::cout << std::endl;
			FAIL("Fail to read data");
		}

		if (buf.getHash() != reference)
		{
			//std::cout << uint128ToString(buf.getHash()) << " " << uint128ToString(reference) << std::endl;
			FAIL("failed on data size " << data_size << " reading by blocks of size " << read_buffer_block_size);
		}
		if (buf.getHash() != out.getHash())
			FAIL("Hash of HashingReadBuffer doesn't match with hash of HashingWriteBuffer on data size " << data_size << " reading by blocks of size " << read_buffer_block_size);
	}
}

int main()
{
	test(5);
	test(100);
	test(2048);
	test(2049);
	test(100000);
	test(1 << 17);

	return 0;
}
